package tools

import (
	"encoding/json"
	"fmt"
	"reflect"
)

/**
 * @author ljfirst
 * @version V1.0
 * @date 2023/7/8 00:17
 * @author-Email ljfirst@mail.ustc.edu.cn
 * @blogURL https://blog.csdn.net/ljfirst
 * @description 工具类方法: 判断相等、打印输出
 *
	EqualsArray(a, b []interface{}) bool
	EqualsArrayStrict(a, b []interface{}) bool
	EqualsMatrix(a, b [][]interface{}) bool
	EqualsMatrixStrict(a, b [][]interface{}) bool

	PrintStruct(interface{})
	PrintIntArray([]int)
	PrintIntMatrix([][]int)
 * */

func EqualsArray(a, b interface{}) bool {
	valueA := reflect.ValueOf(a)
	if valueA.Kind() != reflect.Array && valueA.Kind() != reflect.Slice {
		return false
	}
	valueB := reflect.ValueOf(b)
	if valueB.Kind() != reflect.Array && valueB.Kind() != reflect.Slice {
		return false
	}
	if valueA.Len() != valueB.Len() {
		return false
	}
	length := valueA.Len()
	arrayA, ok1 := a.([]interface{})
	arrayB, ok2 := b.([]interface{})
	if ok1 && ok2 {
		statisticsMap := make(map[interface{}]int)
		for i := 0; i < length; i++ {
			v, exist := statisticsMap[arrayA[i]]
			if !exist {
				statisticsMap[arrayA[i]] = 1
			} else {
				statisticsMap[arrayA[i]] = v + 1
			}
		}
		for i := 0; i < length; i++ {
			v, exist := statisticsMap[arrayB[i]]
			if !exist || v == 0 {
				return false
			} else {
				statisticsMap[arrayB[i]] = v - 1
			}
		}
		return true
	}

	arrayAInt, ok3 := a.([]int)
	arrayBInt, ok4 := b.([]int)
	if ok3 && ok4 {
		statisticsMap := make(map[interface{}]int)
		for i := 0; i < length; i++ {
			v, exist := statisticsMap[arrayAInt[i]]
			if !exist {
				statisticsMap[arrayAInt[i]] = 1
			} else {
				statisticsMap[arrayAInt[i]] = v + 1
			}
		}
		for i := 0; i < length; i++ {
			v, exist := statisticsMap[arrayBInt[i]]
			if !exist || v == 0 {
				return false
			} else {
				statisticsMap[arrayBInt[i]] = v - 1
			}
		}
		return true
	}
	return false
}

func EqualsArrayStrict(a, b interface{}) bool {
	valueA := reflect.ValueOf(a)
	if valueA.Kind() != reflect.Array && valueA.Kind() != reflect.Slice {
		return false
	}
	valueB := reflect.ValueOf(b)
	if valueB.Kind() != reflect.Array && valueB.Kind() != reflect.Slice {
		return false
	}
	if valueA.Len() != valueB.Len() {
		return false
	}
	length := valueA.Len()
	arrayA, oka := a.([]interface{})
	arrayB, okb := b.([]interface{})
	if oka && okb {
		for i := 0; i < length; i++ {
			if arrayA[i] != arrayB[i] {
				return false
			}
		}
		return true
	}
	arrayAI, oka1 := a.([]int)
	arrayBI, okb1 := b.([]int)
	if !oka1 || !okb1 {
		arrayAI2, oka2 := a.([]uint32)
		arrayBI2, okb2 := b.([]uint32)
		if !oka2 || !okb2 {
			return false
		}
		for i := 0; i < length; i++ {
			if arrayAI2[i] != arrayBI2[i] {
				return false
			}
		}
		return true
	}
	for i := 0; i < length; i++ {
		if arrayAI[i] != arrayBI[i] {
			return false
		}
	}
	return true
}

func EqualsMatrix(a, b interface{}) bool {
	ok := checkMatrix(a, b)
	if !ok {
		return false
	}
	arrayA, ok1 := a.([][]interface{})
	arrayB, ok2 := b.([][]interface{})
	if !ok1 || !ok2 {
		arrayA1, ok3 := a.([][]int)
		arrayB1, ok4 := b.([][]int)
		if !ok3 || !ok4 {
			return false
		}
		length := len(arrayA1)
		aFlag, bFlag, testFlag := make([]int, length), make([]int, length), 1
		for i := 0; i < length; i++ {
			for j := 0; j < length; j++ {
				if EqualsArrayStrict(arrayA1[i], arrayB1[j]) && aFlag[i] != 1 && bFlag[j] != 1 {
					aFlag[i], bFlag[j] = 1, 1
				}
			}
		}
		for i := 0; i < length; i++ {
			testFlag &= aFlag[i] & bFlag[i]
		}
		return testFlag == 1
	}
	length := len(arrayA)
	aFlag, bFlag, testFlag := make([]int, length), make([]int, length), 1
	for i := 0; i < length; i++ {
		for j := 0; j < length; j++ {
			if EqualsArrayStrict(arrayA[i], arrayB[j]) && aFlag[i] != 1 && bFlag[j] != 1 {
				aFlag[i], bFlag[j] = 1, 1
			}
		}
	}
	for i := 0; i < length; i++ {
		testFlag &= aFlag[i] & bFlag[i]
	}
	return testFlag == 1
}

func EqualsMatrixStrict(a, b interface{}) bool {
	ok := checkMatrix(a, b)
	if !ok {
		return false
	}
	arrayA, ok1 := a.([][]interface{})
	arrayB, ok2 := b.([][]interface{})
	if !ok1 || !ok2 {
		arrayA1, ok3 := a.([][]int)
		arrayB1, ok4 := b.([][]int)
		if !ok3 || !ok4 {
			return false
		}
		for i := 0; i < len(arrayA1); i++ {
			if !EqualsArrayStrict(arrayA1[i], arrayB1[i]) {
				return false
			}
		}
	}
	for i := 0; i < len(arrayA); i++ {
		if !EqualsArrayStrict(arrayA[i], arrayB[i]) {
			return false
		}
	}
	return true
}

func PrintStruct(obj interface{}) {
	if obj == nil {
		fmt.Println("obj : [nil struct]")
		return
	}
	objShow, err := json.Marshal(obj)
	if err != nil {
		fmt.Printf("obj : [%s]\nerr : [%v]", objShow, err)
		return
	}
	fmt.Printf("obj : %s\n", objShow)
	if string(objShow) == "{}" {
		fmt.Printf("obj : [nil struct or lower case]")
	}
}

func PrintIntArray([]int)    {}
func PrintIntMatrix([][]int) {}

func checkMatrix(a, b interface{}) bool {
	valueA := reflect.ValueOf(a)
	if valueA.Kind() != reflect.Array && valueA.Kind() != reflect.Slice {
		return false
	}
	valueB := reflect.ValueOf(b)
	if valueB.Kind() != reflect.Array && valueB.Kind() != reflect.Slice {
		return false
	}
	return true
}
